/* Copyright (c) [2025] [zhi1208@126.com]
 * SPDX-License-Identifier: MIT
 */

/*********************
 *      INCLUDES
 *********************/
#include <stdint.h>
#include <stddef.h>

#include "stm32f4xx_hal.h"

#include "fal.h"

/*********************
 *      DEFINES
 *********************/
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000)
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000)
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000)
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000)
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000)
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000)
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000)
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000)
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000)
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000)
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000)
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000)

/**********************
 *      TYPEDEFS
 **********************/
typedef enum
{
    FLASH_Sector_0,
    FLASH_Sector_1,
    FLASH_Sector_2,
    FLASH_Sector_3,
    FLASH_Sector_4,
    FLASH_Sector_5,
    FLASH_Sector_6,
    FLASH_Sector_7,
    FLASH_Sector_8,
    FLASH_Sector_9,
    FLASH_Sector_10,
    FLASH_Sector_11,
    FLASH_Sector_MAX
} flash_sector_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static uint32_t stm32_get_sector(uint32_t address);
static uint32_t stm32_get_sector_size(uint32_t sector);
static int init();
static int read(long offset, uint8_t *buf, size_t size);
static int write(long offset, const uint8_t *buf, size_t size);
static int erase(long offset, size_t size);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/
const struct fal_flash_dev stm32f2_onchip_flash =
    {
        .name = "stm32_onchip",
        .addr = 0x08000000,
        .len = 1024 * 1024,
        .blk_size = 128 * 1024,
        .ops = {init, read, write, erase},
        .write_gran = 8};

/**********************
 *   STATIC FUNCTIONS
 **********************/

static uint32_t stm32_get_sector(uint32_t address)
{

    if ((address < ADDR_FLASH_SECTOR_1) && (address >= ADDR_FLASH_SECTOR_0))
        return FLASH_Sector_0;
    else if ((address < ADDR_FLASH_SECTOR_2) && (address >= ADDR_FLASH_SECTOR_1))
        return FLASH_Sector_1;
    else if ((address < ADDR_FLASH_SECTOR_3) && (address >= ADDR_FLASH_SECTOR_2))
        return FLASH_Sector_2;
    else if ((address < ADDR_FLASH_SECTOR_4) && (address >= ADDR_FLASH_SECTOR_3))
        return FLASH_Sector_3;
    else if ((address < ADDR_FLASH_SECTOR_5) && (address >= ADDR_FLASH_SECTOR_4))
        return FLASH_Sector_4;
    else if ((address < ADDR_FLASH_SECTOR_6) && (address >= ADDR_FLASH_SECTOR_5))
        return FLASH_Sector_5;
    else if ((address < ADDR_FLASH_SECTOR_7) && (address >= ADDR_FLASH_SECTOR_6))
        return FLASH_Sector_6;
    else if ((address < ADDR_FLASH_SECTOR_8) && (address >= ADDR_FLASH_SECTOR_7))
        return FLASH_Sector_7;
    else if ((address < ADDR_FLASH_SECTOR_9) && (address >= ADDR_FLASH_SECTOR_8))
        return FLASH_Sector_8;
    else if ((address < ADDR_FLASH_SECTOR_10) && (address >= ADDR_FLASH_SECTOR_9))
        return FLASH_Sector_9;
    else if ((address < ADDR_FLASH_SECTOR_11) && (address >= ADDR_FLASH_SECTOR_10))
        return FLASH_Sector_10;
    else
        return FLASH_Sector_11;
}

static uint32_t stm32_get_sector_size(uint32_t sector)
{
    assert(sector >= 0 && sector < FLASH_Sector_MAX);

    switch (sector)
    {
    case FLASH_Sector_0:
        return 16 * 1024;
    case FLASH_Sector_1:
        return 16 * 1024;
    case FLASH_Sector_2:
        return 16 * 1024;
    case FLASH_Sector_3:
        return 16 * 1024;
    case FLASH_Sector_4:
        return 64 * 1024;
    case FLASH_Sector_5:
        return 128 * 1024;
    case FLASH_Sector_6:
        return 128 * 1024;
    case FLASH_Sector_7:
        return 128 * 1024;
    case FLASH_Sector_8:
        return 128 * 1024;
    case FLASH_Sector_9:
        return 128 * 1024;
    case FLASH_Sector_10:
        return 128 * 1024;
    case FLASH_Sector_11:
        return 128 * 1024;
    default:
        return 128 * 1024;
    }
}

static int init()
{
    return 0;
}

static int read(long offset, uint8_t *buf, size_t size)
{
    size_t i;
    uint32_t addr = stm32f2_onchip_flash.addr + offset;
    for (i = 0; i < size; i++, addr++, buf++)
        *buf = *(uint8_t *)addr;

    return size;
}

static int write(long offset, const uint8_t *buf, size_t size)
{
    size_t i;
    uint32_t read_data;
    uint32_t addr = stm32f2_onchip_flash.addr + offset;

    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
    for (i = 0; i < size; i++, buf++, addr++)
    {
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, addr, *buf);
        read_data = *(uint8_t *)addr;

        if (read_data != *buf)
            return -1;
    }
    HAL_FLASH_Lock();

    return size;
}

static int erase(long offset, size_t size)
{
    size_t erased_size = 0;
    uint32_t cur_erase_sector;
    uint32_t addr = stm32f2_onchip_flash.addr + offset;

    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);

    while (erased_size < size)
    {
        cur_erase_sector = stm32_get_sector(addr + erased_size);

        uint32_t err;
        FLASH_EraseInitTypeDef erase_param = {.TypeErase = FLASH_TYPEERASE_SECTORS,
                                              .Banks = FLASH_BANK_1,
                                              .Sector = cur_erase_sector,
                                              .NbSectors = 1,
                                              .VoltageRange = FLASH_VOLTAGE_RANGE_3};
        HAL_FLASHEx_Erase(&erase_param, &err);

        if (err != 0xFFFFFFFFU)
            return -1;

        erased_size += stm32_get_sector_size(cur_erase_sector);
    }
    HAL_FLASH_Lock();

    return size;
}